#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>

#include "sample-common.h"
#include <imp/imp_encoder.h>

extern void *video_vbm_malloc(int index, int size, int align);
extern void video_vbm_free(int index, void *vaddr);
extern intptr_t video_vbm_virt_to_phys(intptr_t vaddr);
extern intptr_t video_vbm_phys_to_virt(intptr_t paddr);
#define C_ALIGN(x,a)		(((x)+((a)-1))&~((a)-1))

int main(int argc, char *argv[])
{
	int ret = 0;
	int i = 0, frmSize = 0;
	int width = 1920, height = 1088;
	int encNum = 200;
	int out = -1;
	char path[32];
	FILE *inFile = NULL;
	uint8_t *src_buf = NULL;
	void *h = NULL;
	IMPFrameInfo frame;
	IMPEncoderYuvOut stream;
	IMPEncoderYuvIn info;

	memset(&info, 0, sizeof(IMPEncoderYuvIn));

	//encInfo
	info.type = PT_H264;
	info.mode = ENC_RC_MODE_SMART;
	info.frameRate = 25;
	info.gopLength = 50;
	info.initqp = -1;
	info.maxQp = 25;
	info.minQp = 51;
	info.staticTime = 2;
	info.maxBitRate = 1500;
	info.iBiasLvl = 0;
	info.changePos = 80;
	info.qualityLvl = 2;
	info.frmQPStep = 3;
	info.gopQPStep = 15;
	info.gopRelation = false;

	ret = IMP_System_Init();
	if (ret < 0){
		printf("IMP_System_Init failed\n");
		return -1;
	}

	inFile = fopen("1920x1088.nv12", "rb");
	if (inFile == NULL) {
		printf("fopen src file:%s failed\n", "1920x1080.nv12");
		return -1;
	}

    if (info.type == PT_H264) {
		strcpy(path, "out.h264");
	} else {
		printf("T23 not support h265\n");
		return -1;
	}

	out = open(path, O_RDWR | O_CREAT | O_TRUNC, 0644);
	if (out < 0) {
		printf("open out file:%s failed\n", path);
		return -1;
	}

	frmSize = C_ALIGN(width, 16) * C_ALIGN(height, 16) * 3 / 2;
	src_buf = (uint8_t*)video_vbm_malloc(-1, frmSize, 4096);
	if(src_buf == NULL) {
		printf("video_vbm_malloc src_buf failed\n");
		return -1;
	}
	stream.outAddr = (uint8_t *)malloc(frmSize);
	if(stream.outAddr == NULL) {
		printf("steamAddr malloc failed\n");
		return -1;
	}
	ret = IMP_Encoder_YuvInit(&h, width, height, frmSize, (void *)&info);
	if ((ret < 0) || (h == NULL)) {
		printf("%d %d IMP_Encoder_YuvInit failed\n", (ret < 0), (h == NULL));
		return -1;
	}

	frame.width = width;
	frame.height = height;
	frame.size = frmSize;
	frame.phyAddr = (uint32_t)video_vbm_virt_to_phys((intptr_t)src_buf);
	frame.virAddr = (uint32_t)src_buf;

	for (i = 0; i < encNum; i++) {
		if(fread(src_buf, frmSize, 1, inFile) != frmSize) {
			fseek(inFile, 0, SEEK_SET);
			fread(src_buf, 1, frmSize, inFile);
		}

		ret = IMP_Encoder_YuvEncode(h, frmSize, frame, &stream);
		if (ret < 0) {
			printf("IMP_Encoder_YuvEncode failed\n");
			return -1;
		}

		printf("\r%d encode success", i);
		fflush(stdout);
		if (stream.outLen != write(out, stream.outAddr, stream.outLen)) {
			printf("stream write failed\n");
			return -1;
		}
	}
	puts("");

	video_vbm_free(-1, src_buf);
	free(stream.outAddr);
	IMP_Encoder_YuvExit(h);
	close(out);
	fclose(inFile);
	IMP_System_Exit();

	return 0;
}
